/* eslint-disable ts/no-use-before-define */
/* eslint-disable no-console */
import { ref } from 'vue'

const DEFAULT_OPTIONS = {
  url: '', // websocket url
  heartBeatData: '', // 你的心跳数据
  heartBeatInterval: 20 * 1000, // 心跳间隔，单位ms
  reconnectInterval: 10 * 1000, // 断线重连间隔，单位ms
  maxReconnectAttempts: 10, // 最大重连次数
}

export const SocketStatus = {
  Connecting: 'Connecting...', // 表示正在连接，这是初始状态。
  Connected: 'Connected', // 表示连接已经建立。
  Disconnecting: 'Disconnecting', // 表示连接正在关闭。
  Disconnected: 'Disconnected', // 表示连接已经关闭
  End: 'Close',
}

const SocketCloseCode = 1000 // 正常关闭的状态码

// 流程：监听到用户登录 - 进行连接 - 连接成功 - 发送心跳 - 如果心跳超时 - 断开连接 - 重连
// 重连：前几次重连间隔是reconnectIntervalS, 后几次每增加一次重连间隔在之前的基础上增加1S，一共重连10次，10次后还没链接上直接关闭
// 失败：失败的时候也会重连，所以在尝试重连的途中如果有错误返回会增加一条重连，这里的重连会把上面的重连会抵消一部分(连接第一步就是把之前的给关闭，导致后面排队需要重连的给清除了)
// 效果：每次连接失败后会等待10s再次连接，一共重连6次，6次后还没连接上报错然后直接关闭不在尝试

export default function useInitSocket(options = {}) {
  const state: any = {
    options: { ...DEFAULT_OPTIONS, ...options },

    socket: null,
    reconnectAttempts: 0,
    reconnectTimeout: null,

    heartBetaSendTimer: null, // 心跳发送定时器
    heartBetaTimeoutTimer: null, // 心跳超时定时器
  }

  // 连接状态
  const status = ref(SocketStatus.Disconnected)
  const message = ref(null)
  const error = ref(null)

  const onHeartBeat = (callback: () => void) => {
    state.heartBetaSendTimer = setTimeout(() => {
      callback && callback()
      state.heartBetaTimeoutTimer = setTimeout(() => {
        // 心跳超时,直接关闭socket,抛出自定义code=4444, onclose里进行重连
        state.socket.close(4444, 'Heart Beat Timeout')
      }, state.options.heartBeatInterval)
    }, state.options.heartBeatInterval)
  }

  const stopHeartBeat = () => {
    state.heartBetaSendTimer && clearTimeout(state.heartBetaSendTimer)
    state.heartBetaTimeoutTimer && clearTimeout(state.heartBetaTimeoutTimer)
  }

  const stopReconnect = () => {
    state.reconnectTimeout && clearTimeout(state.reconnectTimeout)
  }

  const disconnect = () => {
    if (state.socket && (state.socket.OPEN || state.socket.CONNECTING)) {
      console.log('socket断开连接')
      status.value = SocketStatus.Disconnecting
      state.socket.onmessage = null
      state.socket.onerror = null
      state.socket.onclose = null
      // 发送关闭帧给服务端
      state.socket.close(SocketCloseCode, 'Normal Closure')
      status.value = SocketStatus.Disconnected
      state.socket = null
    }
    stopHeartBeat()
    stopReconnect()
  }

  const reconnect = () => {
    if (status.value === SocketStatus.Connected || status.value === SocketStatus.Connecting)
      return

    stopHeartBeat()
    if (state.reconnectAttempts < state.options.maxReconnectAttempts) {
      console.log('socket重连:', state.reconnectAttempts)

      // 重连间隔，10秒起步，下次递增1秒
      const interval = Math.max(state.options.reconnectInterval, state.reconnectAttempts * 1000)
      console.log('间隔时间：', interval)
      state.reconnectTimeout = setTimeout(() => {
        if (status.value !== SocketStatus.Connected && status.value !== SocketStatus.Connecting)
          connect()
      }, interval)
      state.reconnectAttempts += 1
    }
    else {
      // status.value = SocketStatus.Disconnected
      status.value = SocketStatus.End
      stopReconnect()
      window.$modal.error({
        title: 'Server Error',
        content: 'Websocket is temporarily unavailable. Please try again later.',
        okText: 'Confirm',
        onOk() {
          window.$modal.destroyAll()
        },
      })
    }
  }

  const connect = () => {
    disconnect()

    status.value = SocketStatus.Connecting
    if (!window.navigator.onLine) {
      setTimeout(() => {
        status.value = SocketStatus.Disconnected
      }, 500)
      return
    }

    state.socket = new WebSocket(state.options.url)

    state.socket.onopen = (openEvent: any) => {
      console.log('socket连接:', openEvent)
      state.reconnectAttempts = 0
      status.value = SocketStatus.Connected
      error.value = null
      startHeartBeat()
    }

    state.socket.onmessage = (msgEvent: MessageEvent) => {
      // console.log('socket消息:', msgEvent)

      // 收到任何数据，重新开始心跳
      startHeartBeat()

      const { data } = msgEvent
      const msg = JSON.parse(data)

      message.value = msg
    }

    state.socket.onclose = (closeEvent: any) => {
      console.log('socket关闭:', closeEvent)
      status.value = SocketStatus.Disconnected
      // 非正常关闭,尝试重连
      if (closeEvent.code !== SocketCloseCode)
        reconnect()
    }

    state.socket.onerror = (errEvent: any) => {
      console.log('socket报错:', errEvent)
      status.value = SocketStatus.Disconnected
      error.value = errEvent
      // 连接失败，尝试重连
      reconnect()
    }
  }

  const startHeartBeat = () => {
    stopHeartBeat()
    onHeartBeat(() => {
      if (status.value === SocketStatus.Connected) {
        state.socket.send(state.options.heartBeatData)
        console.log('socket心跳发送:', state.options.heartBeatData)
      }
    })
  }

  return {
    status,
    message,
    error,
    connect,
    reconnect,
    disconnect,
  }
}
